package org.nativejpa.mapping;

import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;

import javax.persistence.PersistenceException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * Factory to create Entity Object according to Entity Class and Entity data
 * @author <a href="mailto:jfox.young@gmail.com">Young Yang</a>
 */
public class EntityFactory {

    private static final Log log = LogFactory.getLog(EntityFactory.class);

    private static EntityRepository entityRepository = EntityRepository.getInstance();

    public static <T> T newEntityObject(Class<T> resultClass){
        return newEntityObject(resultClass, new HashMap<String, Object>(0));
    }

    public static MappingEntity newMappingEntity(Map<String, Object> resultMap){
        return MappingEntity.newMappedEntity(resultMap);
    }

    public static <T> T newEntityObject(Class<T> resultClass, Map<String, Object> resultMap) {
        // 从 SQLTemplate 中获得 resultClass 的对应信息，然后Field.set
        T entity;
        try {
            entity = resultClass.newInstance();
        }
        catch (Exception e) {
            throw new PersistenceException("new Entity for Class: " + resultClass.getName() + " failed.", e);
        }

        for (Map.Entry<String, Object> entry : resultMap.entrySet()) {
            String columnName = entry.getKey();
            //check the name exists in resultMap, but not in resultClass
            if(!entityRepository.isColumnExisted(resultClass,columnName)) {
                log.warn("No column named \"" + columnName + "\" in result class " + resultClass.getName() + ", ignored.");
            }
            else {
                ColumnMetadata columnMetadata = entityRepository.getColumnMetadata(resultClass, columnName);
                if(!columnMetadata.isJoinColumn()) {
                    // join column will be set by appendJoinColumnValue
                    columnMetadata.setFieldValue(entity, entry.getValue());
                }
            }
        }
        return entity;
    }

    public static <T> T newEntityObject(Class<T> resultClass, Map<String, Object> resultMap, Map<String, Object> joinColumnResultMap) {
        //TODO:
        return null;
    }

    public static <T> T updateEntityObject(T oldEntity, T newEntity) {
        Collection<ColumnMetadata> columnMetas =  EntityRepository.getInstance().getAllColumnMetadatas(oldEntity.getClass());
        for(ColumnMetadata columnMetadata : columnMetas){
            Object newValue = columnMetadata.getFieldValue(newEntity) ;
            columnMetadata.setFieldValue(oldEntity, newValue);
        }
        return oldEntity;
    }

    /**
     * append JoinColumn into EntityObject
     * @param entity
     * @param joinColumnResultMap
     */
    public static void appendJoinColumnValue(Object entity, Map<String, Object> joinColumnResultMap) {
        if(entity instanceof Map) {
            ((Map)entity).putAll(joinColumnResultMap);
        }
        else if (entity.getClass().equals(MappingEntity.class)) {
            for (Map.Entry<String, Object> entry : joinColumnResultMap.entrySet()) {
                ((MappingEntity)entity).setColumnValue(entry.getKey(), entry.getValue());
            }
        }
        else {
            for (Map.Entry<String, Object> entry : joinColumnResultMap.entrySet()) {
                String columnName = entry.getKey();
                ColumnMetadata columnMetadata = entityRepository.getColumnMetadata(entity.getClass(), columnName);
                if (columnMetadata != null) {
                    columnMetadata.setFieldValue(entity, entry.getValue());
                }
            }
        }
    }
}
